package com.yunos.camera;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.List;

import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ClipboardManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences.Editor;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PreviewCallback;
import android.hardware.Camera.Size;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.location.Location;
import android.media.AudioManager;
import android.media.SoundPool;
import android.net.Uri;
import android.os.ConditionVariable;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.OrientationEventListener;
import android.view.SurfaceHolder;
import android.view.View;
import android.view.WindowManager;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.Result;
import com.way.camera.R;
import com.yunos.camera.CameraManager.CameraProxy;
import com.yunos.camera.CameraPreference.OnPreferenceChangedListener;
import com.yunos.camera.network.NetworkUtil;
import com.yunos.camera.platform.PlatformHelper;
import com.yunos.camera.qrcode.DecodeHandler;
import com.yunos.camera.qrcode.DecodeThread;
import com.yunos.camera.ui.CameraSurfaceView;
import com.yunos.camera.ui.ScanCard;

public class ScanModule extends CameraModule implements ScanController,
		FocusOverlayManager.Listener, OnPreferenceChangedListener,
		SensorEventListener, PreviewCallback, ScanCard.ActionListener {

	private static final String TAG = "CAM_ScanModule";
	private static final String CODETYPE_BARCODE = "barcode";
	private static final String CODETYPE_QRCODE = "qrcode";
	private static final String CODETYPE_NONE = "none";

	private static final int SETUP_PREVIEW = 1;
	private static final int FIRST_TIME_INIT = 2;
	private static final int CLEAR_SCREEN_DELAY = 3;
	private static final int SET_CAMERA_PARAMETERS_WHEN_IDLE = 4;
	private static final int CHECK_DISPLAY_ROTATION = 5;
	private static final int SHOW_TAP_TO_FOCUS_TOAST = 6;

	private static final int SWITCH_CAMERA_START_ANIMATION = 8;
	private static final int CAMERA_OPEN_DONE = 9;
	private static final int START_PREVIEW_DONE = 10;
	private static final int OPEN_CAMERA_FAIL = 11;
	private static final int CAMERA_DISABLED = 12;
	private static final int CAPTURE_ANIMATION_DONE = 13;
	private static final int START_PREVIEW_ANIMATION_DONE = 14;
	private static final int CHANGE_MODULE = 17;
	private static final int DO_SCAN = 18;
	private static final int CALL_AUTOFOCUS = 19;
	private static final int RESTART_SCAN = 20;

	private static final int SCAN_INTERVAL = 0;
	private static final int AUTOFOCUS_INTERVAL = 1000;
	// The subset of parameters we need to update in setCameraParameters().
	private static final int UPDATE_PARAM_INITIALIZE = 1;
	private static final int UPDATE_PARAM_ZOOM = 2;
	private static final int UPDATE_PARAM_PREFERENCE = 4;
	private static final int UPDATE_PARAM_ALL = -1;

	// This is the timeout to keep the camera in onPause for the first time
	// after screen on if the activity is started from secure lock screen.
	private static final int KEEP_CAMERA_TIMEOUT = 1000; // ms

	private static final String CONTENT_TYPE_NONE = "none";
	private static final String CONTENT_TYPE_LINK = "weblink";
	private static final String CONTENT_TYPE_VCARD = "vcard";
	private static final String CONTENT_TYPE_TEXT = "text";
	private static final String CONTENT_TYPE_ALIPAY = "alipay";
	private static final String CONTENT_TYPE_LAIWANG = "laiwang";

	// copied from Camera hierarchy
	private CameraActivity mActivity;
	private CameraProxy mCameraDevice;
	private int mCameraId;
	private Parameters mParameters;
	private boolean mPaused = false;
	private ComboPreferences mPreferences;
	private ScanUI mUI;
	private SoundPool mSoundPool;
	private int mSoundId;
	private int mResultType = DecodeHandler.RESULT_NONE;
	private boolean mIsAutoFocusCallback = false;
	private final Object mAutoFocusMoveCallback = new AutoFocusMoveCallback();
	private final AutoFocusCallback mAutoFocusCallback = new AutoFocusCallback();
	// these are only used by Camera

	// The activity is going to switch to the specified camera id. This is
	// needed because texture copy is done in GL thread. -1 means camera is not
	// switching.
	protected int mPendingSwitchCameraId = -1;
	private boolean mOpenCameraFail;
	private boolean mCameraDisabled;
	private boolean mCameraStartedFirstTime = false;
	private boolean mIsChangeingModule = false;

	// When setCameraParametersWhenIdle() is called, we accumulate the subsets
	// needed to be updated in mUpdateSet.
	private int mUpdateSet;
	private FocusOverlayManager mFocusManager;
	private static final int SCREEN_DELAY = 2 * 60 * 1000;

	private int mZoomValue; // The current zoom value.

	private Parameters mInitialParams;
	private boolean mFocusAreaSupported;
	private boolean mMeteringAreaSupported;
	private boolean mAeLockSupported;
	private boolean mAwbLockSupported;

	// These latency time are for the CameraLatency test.
	public long mAutoFocusTime;
	public long mShutterLag;
	public long mShutterToPictureDisplayedTime;
	public long mPictureDisplayedToJpegCallbackTime;
	public long mJpegCallbackFinishTime;
	public long mCaptureStartTime;

	// The degrees of the device rotated clockwise from its natural orientation.
	private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
	private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();

	private PreferenceGroup mPreferenceGroup;
	private byte[] mPreviewData;
	// private Bitmap previewBitmap;
	private int mPendingModuleIndex = CameraActivity.UNKNOWN_MODULE_INDEX;

	// The display rotation in degrees. This is only valid when mCameraState is
	// not PREVIEW_STOPPED.
	private int mDisplayRotation;
	// The value for android.hardware.Camera.setDisplayOrientation.
	private int mCameraDisplayOrientation;
	// The value for UI components like indicators.
	private int mDisplayOrientation;
	// The value for android.hardware.Camera.Parameters.setRotation.
	private boolean mFirstTimeInitialized;

	private LocationManager mLocationManager;

	private SensorManager mSensorManager;
	private float[] mGData = new float[3];
	private float[] mMData = new float[3];
	private float[] mR = new float[16];
	private int mHeading = -1;
	// private boolean isSurfaceTextureNull;
	private boolean mIsScanning = true;
	private final MainHandler mHandler = new MainHandler();

	private ConditionVariable mStartPreviewPrerequisiteReady = new ConditionVariable();
	private boolean isFullscreen;
	private boolean mIsScanIntent = false;
	private boolean mIsRecordLocation = false;

	// Temp variables for scan result
	private Location mLocation;
	private String mScanResult;
	private String mUuid;
	private String mShareUrl;
	private boolean mIsScanCardInitialized = false;

	// UT Variables
	private String mFlashMode;
	private String mUTCodeType = CODETYPE_NONE;
	private String mUTContentType = CONTENT_TYPE_NONE;

	private long mScanTime;

	private DecodeThread decodeThread;

	private enum State {
		PREVIEW, SUCCESS, DONE
	}

	private State mState;

	/**
	 * This Handler is used to post message back onto the main thread of the
	 * application
	 */
	private class MainHandler extends Handler {

		public MainHandler() {

		}

		@Override
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case SETUP_PREVIEW: {
				startPreview();
				break;
			}

			case CLEAR_SCREEN_DELAY: {
				mActivity.getWindow().clearFlags(
						WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
				break;
			}

			case FIRST_TIME_INIT: {
				initializeFirstTime();
				break;
			}

			case SET_CAMERA_PARAMETERS_WHEN_IDLE: {
				setCameraParametersWhenIdle(0);
				break;
			}

			case CHECK_DISPLAY_ROTATION: {
				// Set the display orientation if display rotation has
				// changed.
				// Sometimes this happens when the device is held upside
				// down and camera app is opened. Rotation animation will
				// take some time and the rotation value we have got may be
				// wrong. Framework does not have a callback for this now.
				if (Util.getDisplayRotation(mActivity) != mDisplayRotation) {
					setDisplayOrientation();
				}
				/*
				 * if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
				 * mHandler.sendEmptyMessageDelayed(CHECK_DISPLAY_ROTATION,
				 * 100); }
				 */
				break;
			}

			case SHOW_TAP_TO_FOCUS_TOAST: {
				// showTapToFocusToast();
				break;
			}

			case SWITCH_CAMERA_START_ANIMATION: {
				// ((CameraScreenNail)
				// mActivity.mCameraScreenNail).animateSwitchCamera();
				break;
			}

			case CAMERA_OPEN_DONE: {
				onCameraOpened();
				break;
			}

			case START_PREVIEW_DONE: {
				onPreviewStarted();
				break;
			}

			case START_PREVIEW_ANIMATION_DONE: {
				// mActivity.getGLRootView().startFadeOut();
				mActivity
						.setCameraState(CameraActivity.CAMERA_STATE_PREVIEWING);
				break;
			}

			case OPEN_CAMERA_FAIL: {
				mOpenCameraFail = true;
				Util.showErrorAndFinish(mActivity,
						R.string.cannot_connect_camera);
				break;
			}

			case CAMERA_DISABLED: {
				mCameraDisabled = true;
				// Util.showErrorAndFinish(mActivity, R.string.camera_disabled);
				break;
			}

			case CAPTURE_ANIMATION_DONE: {
				// mUI.enablePreviewThumb(false);
				break;
			}

			case DO_SCAN:
				Log.d("dyb", "START_SCAN");
				if (mCameraDevice != null) {
					mCameraDevice.setOneshotPreviewCallback(ScanModule.this);
					mScanTime = System.currentTimeMillis();
				}
				break;

			case RESTART_SCAN:
				restartDecode();
				break;

			case CALL_AUTOFOCUS:
				autoFocus();
				removeMessages(CALL_AUTOFOCUS);
				break;

			case R.id.decode_failed:
				removeMessages(DO_SCAN);
				sendEmptyMessageDelayed(DO_SCAN, SCAN_INTERVAL);
				// QRCodeResult resultFail = (QRCodeResult) msg.obj;
				// int location[] = resultFail.location;
				mResultType = DecodeHandler.RESULT_NONE;
				// mUI.setPoints(location);
				break;

			case R.id.decode_succeeded:
				removeMessages(DO_SCAN);
				removeMessages(CALL_AUTOFOCUS);
				Result resultSuccess = (Result) msg.obj;
				String str = resultSuccess.getText();
				BarcodeFormat format = resultSuccess.getBarcodeFormat();
				if (BarcodeFormat.QR_CODE == format) {
					mResultType = DecodeHandler.RESULT_QR;
				} else {
					mResultType = DecodeHandler.RESULT_BAR;
				}
				mState = State.SUCCESS;
				// if (resultSuccess.location[16] == 0)
				// mResultType = DecodeHandler.RESULT_QR;
				// else if (resultSuccess.location[16] == 1)
				// mResultType = DecodeHandler.RESULT_BAR;
				// play scan success sound
				playSuccessSound();
				onDecodeSuccess(str);
				break;

			case CHANGE_MODULE: {
				mActivity.doChangeCamera(mPendingModuleIndex);
				mPendingModuleIndex = CameraActivity.UNKNOWN_MODULE_INDEX;
				break;
			}

			}
		}
	}

	protected void setCameraId(int cameraId) {
		ListPreference pref = mPreferenceGroup
				.findPreference(CameraSettings.KEY_CAMERA_ID);
		pref.setValue("" + cameraId);
	}

	private void initializeFocusManager() {
		// Create FocusManager object. startPreview needs it.
		// if mFocusManager not null, reuse it
		// otherwise create a new instance
		if (mFocusManager != null) {
			mFocusManager.removeMessages();
		} else {
			CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
			boolean mirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
			String[] defaultFocusModes = mActivity
					.getResources()
					.getStringArray(R.array.pref_camera_focusmode_default_array);
			mFocusManager = new FocusOverlayManager(mPreferences,
					defaultFocusModes, mInitialParams, this, mirror,
					mActivity.getMainLooper(), mUI);
		}
	}

	private void initializeCapabilities() {
		mInitialParams = mCameraDevice.getParameters();
		mFocusAreaSupported = Util.isFocusAreaSupported(mInitialParams);
		mMeteringAreaSupported = Util.isMeteringAreaSupported(mInitialParams);
		mAeLockSupported = Util.isAutoExposureLockSupported(mInitialParams);
		mAwbLockSupported = Util
				.isAutoWhiteBalanceLockSupported(mInitialParams);
	}

	// Snapshots can only be taken after this is called. It should be called
	// once only. We could have done these things in onCreate() but we want to
	// make preview screen appear as soon as possible.
	private void initializeFirstTime() {
		if (mFirstTimeInitialized)
			return;
		initializeLocationSettings();
		mUI.initializeFirstTime();
		mFirstTimeInitialized = true;
	}

	// If the activity is paused and resumed, this method will be called in
	// onResume.
	private void initializeSecondTime() {
		initializeLocationSettings();
		mUI.initializeSecondTime(mParameters);
	}

	private void setFlashParameters() {
		mFlashMode = mPreferences.getString(
				CameraSettings.KEY_SCANCAMERA_FLASH_MODE,
				mActivity.getString(R.string.pref_scan_flashmode_default));
		List<String> supportedFlash = mParameters.getSupportedFlashModes();
		if (Util.isSupported(mFlashMode, supportedFlash)) {
			mParameters.setFlashMode(mFlashMode);
		} else {
			mFlashMode = mParameters.getFlashMode();
			if (mFlashMode == null) {
				mFlashMode = mActivity
						.getString(R.string.pref_camera_flashmode_no_flash);
			}
		}
		mParameters.setFocusMode(Parameters.FOCUS_MODE_AUTO);
		if (mCameraDevice != null)
			mCameraDevice.setParameters(mParameters);
	}

	private void setCameraParameters(int updateSet) {
		if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
			updateCameraParametersInitialize();
		}

		if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
			updateCameraParametersZoom();
		}

		if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
			updateCameraParametersPreference();
			mIsAutoFocusCallback = false;
		}

		mCameraDevice.setParameters(mParameters);
	}

	// If the Camera is idle, update the parameters immediately, otherwise
	// accumulate them in mUpdateSet and update later.
	private void setCameraParametersWhenIdle(int additionalUpdateSet) {
		mUpdateSet |= additionalUpdateSet;
		if (mCameraDevice == null) {
			// We will update all the parameters when we open the device, so
			// we don't need to do anything now.
			mUpdateSet = 0;
			return;
		} else if (isCameraIdle()) {
			setCameraParameters(mUpdateSet);
			// updateSceneMode();
			mUpdateSet = 0;
		} else {
			if (!mHandler.hasMessages(SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
				mHandler.sendEmptyMessageDelayed(
						SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
			}
		}
	}

	private void updateCameraParametersInitialize() {
		// Reset preview frame rate to the maximum because it may be lowered by
		// video camera application.
		List<Integer> frameRates = mParameters.getSupportedPreviewFrameRates();
		if (frameRates != null) {
			Integer max = Collections.max(frameRates);
			mParameters.setPreviewFrameRate(max);
		}

		mParameters.set(Util.RECORDING_HINT, Util.FALSE);

		// Disable video stabilization. Convenience methods not available in API
		// level <= 14
		String vstabSupported = mParameters
				.get("video-stabilization-supported");
		if ("true".equals(vstabSupported)) {
			mParameters.set("video-stabilization", "false");
		}
	}

	private void updateCameraParametersZoom() {
		// Set zoom.
		if (mParameters.isZoomSupported()) {
			mParameters.setZoom(mZoomValue);
		}
	}

	private void updateCameraParametersPreference() {
		Log.v(TAG, "updateCameraParametersPreference");
		setAutoExposureLockIfSupported();
		setAutoWhiteBalanceLockIfSupported();
		setFocusAreasIfSupported();
		setMeteringAreasIfSupported();

		Size pictureSize = mParameters.getPictureSize();
		int maxPix = 0;
		List<Size> supported = mParameters.getSupportedPictureSizes();
		for (Size s : supported) {
			if (s.width * s.height > maxPix) {
				maxPix = s.width * s.height;
				pictureSize = s;
			}
		}
		mParameters.setPictureSize(pictureSize.width, pictureSize.height);

		Size original = mParameters.getPreviewSize();
		Size optimalSize = PlatformHelper.getScanResolution(mParameters
				.getSupportedPreviewSizes());
		Log.v(TAG, "current preview size(" + original.width + "x"
				+ original.height + ")");
		Log.v(TAG, "optimal preview size(" + optimalSize.width + "x"
				+ optimalSize.height + ")");
		if (!original.equals(optimalSize)) {
			mParameters.setPreviewSize(optimalSize.width, optimalSize.height);
			// Zoom related settings will be changed for different preview
			// sizes, so set and read the parameters to get latest values
			if (mHandler.getLooper() == Looper.myLooper()) {
				// On UI thread only, not when camera starts up
				// startPreview();
			} else {
				// mCameraDevice.setParameters(mParameters);
			}
			mParameters = mCameraDevice.getParameters();
		}

		mParameters.setAntibanding(Parameters.ANTIBANDING_AUTO);
		mFocusManager.overrideFocusMode(null);
		mParameters.setFocusMode(mFocusManager.getFocusMode());
		updateAutoFocusMoveCallback();
	}

	private void setAutoExposureLockIfSupported() {
		if (mAeLockSupported) {
			mParameters.setAutoExposureLock(mFocusManager.getAeAwbLock());
		}
	}

	private void setAutoWhiteBalanceLockIfSupported() {
		if (mAwbLockSupported) {
			mParameters.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
		}
	}

	private void setFocusAreasIfSupported() {
		if (mFocusAreaSupported && !mIsAutoFocusCallback) {
			mParameters.setFocusAreas(mFocusManager.getFocusAreas());
		}
	}

	private void setMeteringAreasIfSupported() {
		if (mMeteringAreaSupported && !mIsAutoFocusCallback) {
			// Use the same area for focus and metering.
			mParameters.setMeteringAreas(mFocusManager.getMeteringAreas());
		}
	}

	private void onPreviewStarted() {
		Log.v(TAG, "onPreviewStarted");
		mParameters = mCameraDevice.getParameters();
		Size previewSize = mParameters.getPreviewSize();
		mActivity.getGLRootView().setPreviewSize(previewSize.width,
				previewSize.height, true);
		startFaceDetection();
		setFlashParameters();
		mUI.initializeFlash(mParameters);
		// mHandler.sendEmptyMessageDelayed(START_PREVIEW_ANIMATION_DONE, 100);
		mActivity.setCameraState(CameraActivity.CAMERA_STATE_PREVIEWING);
	}

	private void onCameraOpened() {
		Log.v(TAG, "onCameraOpened");
		mParameters = mCameraDevice.getParameters();
		mStartPreviewPrerequisiteReady.block();
		startPreview();
		int size[] = new int[3];
		mActivity.getGLRootView().getDrawingAreaSize(size);
		mFocusManager.setPreviewSize(size[0], size[1], size[2]);
		mSoundPool = new SoundPool(1, AudioManager.STREAM_MUSIC, 0);
		mSoundId = mSoundPool.load(mActivity, R.raw.scan_success, 1);
		mSoundPool.setVolume(mSoundId, 1.0f, 1.0f);

		openCameraCommon();
	}

	public boolean isCameraIdle() {
		return (mActivity.getCameraState() == CameraActivity.CAMERA_STATE_PREVIEWING)
				|| (mActivity.getCameraState() == CameraActivity.CAMERA_STATE_PREVIEW_STOPPED)
				|| ((mFocusManager != null) && mFocusManager.isFocusCompleted());
	}

	// This can be called by UI Thread or CameraStartUpThread. So this should
	// not modify the views.
	private void startPreview() {
		Log.v(TAG, "startPreview");
		mCameraDevice.setErrorCallback(mErrorCallback);
		mState = State.PREVIEW;
		// ICS camera frameworks has a bug. Face detection state is not cleared
		// after taking a picture. Stop the preview to work around it. The bug
		// was fixed in JB.
		if (mActivity.getCameraState() != CameraActivity.CAMERA_STATE_PREVIEW_STOPPED) {
			stopPreview();
		}

		setDisplayOrientation();
		setCameraParameters(UPDATE_PARAM_ALL);
		// isSurfaceTextureNull = true;
		if (ApiHelper.HAS_SURFACE_TEXTURE) {
			CameraSurfaceView mSurfaceView = (CameraSurfaceView) mActivity
					.getGLRootView();
			if (mUI.getSurfaceTexture() == null) {
				mUI.setSurfaceTexture(mSurfaceView.getSurfaceTexture());
			} else {
				// mSurfaceView.setPreviewSize(size.width, size.height);
			}
			mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation);
			Object st = mUI.getSurfaceTexture();
			if (st != null) {
				mCameraDevice.setPreviewTextureAsync((SurfaceTexture) st);
				// isSurfaceTextureNull = false;
			} else {
				return;
			}
		} else {
			mCameraDevice.setDisplayOrientation(mDisplayOrientation);
		}

		if (mActivity.getCameraState() == CameraActivity.CAMERA_STATE_PREVIEW_STOPPED) {
			Log.v(TAG, "CameraDevice startPreview");
			mCameraDevice.startPreviewAsync();
		}

		mFocusManager.onPreviewStarted();
		mActivity.getGLRootView().startPreview(mCameraId);
		// if (mSnapshotOnIdle) {
		// mHandler.post(mDoSnapRunnable);
		// }
		setFlashParameters();
		if (mIsScanning) {
			mHandler.sendEmptyMessage(RESTART_SCAN);
		}
		mHandler.sendEmptyMessage(START_PREVIEW_DONE);
		mHandler.sendEmptyMessage(CHECK_DISPLAY_ROTATION);
	}

	// either open a new camera or switch cameras
	private void openCameraCommon() {
		loadCameraPreferences();
		mUI.onCameraOpened(mPreferenceGroup, mPreferences, mParameters, this);
	}

	private void setDisplayOrientation() {
		mDisplayRotation = Util.getDisplayRotation(mActivity);
		mDisplayOrientation = Util.getDisplayOrientation(mDisplayRotation,
				mCameraId);
		mCameraDisplayOrientation = Util.getDisplayOrientation(0, mCameraId);
		// mUI.setDisplayOrientation(mDisplayOrientation);
		if (mFocusManager != null) {
			mFocusManager.setDisplayOrientation(mDisplayOrientation);
		}
	}

	@Override
	public void onStateChanged() {
		switch (mActivity.getCameraState()) {
		case CameraActivity.CAMERA_STATE_PREVIEW_STOPPED:
		case CameraActivity.CAMERA_STATE_SWITCHING_CAMERA:
			mUI.enableGestures(false);
			break;
		case CameraActivity.CAMERA_STATE_PREVIEWING:
		case CameraActivity.CAMERA_STATE_FOCUSING:
			mUI.enableGestures(true);
			break;
		}
	}

	private int getPreferredCameraId(ComboPreferences preferences) {
		int intentCameraId = Util.getCameraFacingIntentExtras(mActivity);
		if (intentCameraId != -1) {
			// Testing purpose. Launch a specific camera through the intent
			// extras.
			return intentCameraId;
		} else {
			// In Scan Module, User can only use back camera.
			CameraSettings.writePreferredCameraId(mPreferences.getGlobal(), 0);
			return 0;
		}
	}

	@Override
	public void init(CameraActivity activity, View root) {
		Log.v(TAG, "init module");
		super.init(activity, root);
		mActivity = activity;
		isFullscreen = false;
		mPendingModuleIndex = -1;
		mIsScanIntent = Util.isScanIntent(mActivity.getIntent());
		DisplayMetrics dm = new DisplayMetrics();
		mActivity.getWindowManager().getDefaultDisplay().getMetrics(dm);
		mActivity.getGLRootView()
				.setScreenSize(dm.widthPixels, dm.heightPixels);
		// mActivity.getGLRootView().setFullScreenMode(isFullscreen);
		mPreferences = new ComboPreferences(mActivity);
		// CameraSettings.upgradeGlobalPreferences(mPreferences.getGlobal());
		mCameraId = getPreferredCameraId(mPreferences);
		// Surface texture is from camera screen nail and startPreview needs it.
		// This must be done before startPreview.
		mPreferences.setLocalId(mActivity, mCameraId);
		// CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());

		mUI = new ScanUI(activity, this, root, mPreferences);
		mUI.setListener(this);

		resetExposureCompensation();
		mStartPreviewPrerequisiteReady.open();

		mLocationManager = new LocationManager(mActivity, mUI);
		mSensorManager = (SensorManager) (mActivity
				.getSystemService(Context.SENSOR_SERVICE));

		mActivity.getGLRootView().setFilter(0, null);

		mCameraDevice = mActivity.getCameraDevice();
		if (mCameraDevice == null) {
			if (activity.isPaused()) {
				Log.v(TAG, "activity is paused, so cancel init");
			} else {
				mHandler.sendEmptyMessage(OPEN_CAMERA_FAIL);
			}
		} else {
			stopPreviewAfterInit();
			mHandler.sendEmptyMessage(CAMERA_OPEN_DONE);
			initializeCapabilities();
			if (mFocusManager == null)
				initializeFocusManager();
		}
		Log.v(TAG, "init module end");
	}

	private void stopPreviewAfterInit() {
		mParameters = mCameraDevice.getParameters();
		Size original = mParameters.getPreviewSize();
		Size optimalSize = PlatformHelper.getScanResolution(mParameters
				.getSupportedPreviewSizes());
		Log.v(TAG, "current preview size(" + original.width + "x"
				+ original.height + ")");
		Log.v(TAG, "optimal preview size(" + optimalSize.width + "x"
				+ optimalSize.height + ")");
		if (!original.equals(optimalSize)) {
			Log.v(TAG, "stop preview after init");
			mCameraDevice.stopPreview();
			mActivity
					.setCameraState(CameraActivity.CAMERA_STATE_PREVIEW_STOPPED);
		}

		mActivity.setCameraState(CameraActivity.CAMERA_STATE_PREVIEW_STOPPED);
	}

	@Override
	public void onFullScreenChanged(boolean full) {
	}

	@Override
	public void onPauseBeforeSuper() {
		Log.d("dyb_scan", "onPauseBeforeSuper");
		super.onPauseBeforeSuper();
		if (mUI != null)
			mUI.stopAnimation();
		mPaused = true;
		if (decodeThread != null) {
			Message message = decodeThread.getHandler()
					.obtainMessage(R.id.quit);
			message.sendToTarget();
		}
		if (mCameraDevice != null) {
			mCameraDevice.setPreviewCallback(null);
		}
		if (mSensorManager != null) {
			Sensor gsensor = mSensorManager
					.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
			if (gsensor != null) {
				mSensorManager.unregisterListener(this, gsensor);
			}

			Sensor msensor = mSensorManager
					.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
			if (msensor != null) {
				mSensorManager.unregisterListener(this, msensor);
			}
		}
	}

	@Override
	public void onPauseAfterSuper() {
		Log.d(TAG, "onPauseAfterSuper");
		// When camera is started from secure lock screen for the first time
		// after screen on, the activity gets
		// onCreate->onResume->onPause->onResume.
		// To reduce the latency, keep the camera for a short time so it does
		// not need to be opened again.
		if (mCameraDevice != null && mActivity.isSecureCamera()
				&& ActivityBase.isFirstStartAfterScreenOn()) {
			ActivityBase.resetFirstStartAfterScreenOn();
		}
		// Reset the focus first. Camera CTS does not guarantee that
		// cancelAutoFocus is allowed after preview stops.
		if (mCameraDevice != null
				&& mActivity.getCameraState() != CameraActivity.CAMERA_STATE_PREVIEW_STOPPED) {
			mCameraDevice.cancelAutoFocus();
		}

		if (mLocationManager != null) {
			mLocationManager.recordLocation(false);
		}

		// If we are in an image capture intent and has taken
		// a picture, we just clear it in onPause.
		// mJpegImageData = null;
		// Remove the messages in the event queue.
		mHandler.removeMessages(SETUP_PREVIEW);
		mHandler.removeMessages(FIRST_TIME_INIT);
		mHandler.removeMessages(CHECK_DISPLAY_ROTATION);
		mHandler.removeMessages(SWITCH_CAMERA_START_ANIMATION);
		mHandler.removeMessages(CAMERA_OPEN_DONE);
		mHandler.removeMessages(START_PREVIEW_DONE);
		mHandler.removeMessages(OPEN_CAMERA_FAIL);
		mHandler.removeMessages(CAMERA_DISABLED);
		mHandler.removeMessages(START_PREVIEW_ANIMATION_DONE);
		mHandler.removeMessages(R.id.decode);
		mHandler.removeMessages(R.id.decode_succeeded);
		mHandler.removeMessages(R.id.decode_failed);
		mHandler.removeMessages(DO_SCAN);
		mHandler.removeMessages(CALL_AUTOFOCUS);
		mHandler.removeMessages(RESTART_SCAN);

		if (mFocusManager != null)
			mFocusManager.removeMessages();

		if (mCameraDevice != null) {
			mCameraDevice.setPreviewCallback(null);
			mCameraDevice.setErrorCallback(null);
			mCameraDevice.setOneshotPreviewCallback(null);
			mCameraDevice.setAutoFocusMoveCallback(null);
			mCameraDevice = null;
			mActivity
					.setCameraState(CameraActivity.CAMERA_STATE_PREVIEW_STOPPED);
			if (mFocusManager != null)
				mFocusManager.onCameraReleased();
		}

		resetScreenOn();
		// mUI.onPause();

		mPendingSwitchCameraId = -1;
		MediaSaveService s = mActivity.getMediaSaveService();
		if (s != null) {
			s.setListener(null);
		}

		if (mSoundPool != null && mSoundPool.unload(mSoundId)) {
			mSoundPool = null;
		}
		mIsAutoFocusCallback = false;
	}

	public boolean isProcessScanResult() {
		boolean ret = mActivity.getIntent().getBooleanExtra(
				ScanActivity.EXTRA_SCAN_PROCESS_RESULT, false);
		return ret;
	}

	private void resetExposureCompensation() {
		String value = mPreferences.getString(CameraSettings.KEY_EXPOSURE,
				CameraSettings.EXPOSURE_DEFAULT_VALUE);
		if (!CameraSettings.EXPOSURE_DEFAULT_VALUE.equals(value)) {
			Editor editor = mPreferences.edit();
			editor.putString(CameraSettings.KEY_EXPOSURE, "0");
			editor.apply();
		}
	}

	private void resetScreenOn() {
		mHandler.removeMessages(CLEAR_SCREEN_DELAY);
		mActivity.getWindow().clearFlags(
				WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
	}

	private void keepScreenOnAwhile() {
		mHandler.removeMessages(CLEAR_SCREEN_DELAY);
		mActivity.getWindow().addFlags(
				WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
		mHandler.sendEmptyMessageDelayed(CLEAR_SCREEN_DELAY, SCREEN_DELAY);
	}

	@Override
	public void onResumeBeforeSuper() {
		Log.d("dyb_scan", "onResumeBeforeSuper");
		mPaused = false;
	}

	@Override
	public void onResumeAfterSuper() {
		Log.d(TAG, "onResumeAfterSuper");

		if (mCameraDevice == null) {
			mCameraDevice = mActivity.getCameraDevice();
			if (mCameraDevice == null) {
				if (mActivity.isPaused()) {
					Log.v(TAG,
							"activity is paused, so cancel onResumeBeforeSuper");
					return;
				} else {
					mHandler.sendEmptyMessage(OPEN_CAMERA_FAIL);
					return;
				}
			} else {
				mHandler.sendEmptyMessage(CAMERA_OPEN_DONE);
				initializeCapabilities();
				if (mFocusManager == null)
					initializeFocusManager();
			}
		}

		if (mOpenCameraFail || mCameraDisabled)
			return;
		decodeThread = new DecodeThread(mActivity, ScanModule.this);
		decodeThread.start();
		// mJpegPictureCallbackTime = 0;
		mZoomValue = 0;
		// Start the preview if it is not started.
		if (mActivity.getCameraState() == CameraActivity.CAMERA_STATE_PREVIEW_STOPPED) {
			resetExposureCompensation();
		}

		// If first time initialization is not finished, put it in the
		// message queue.
		if (!mFirstTimeInitialized) {
			mHandler.sendEmptyMessage(FIRST_TIME_INIT);
		} else {
			initializeSecondTime();
		}
		keepScreenOnAwhile();
		// Initializing it here after the preview is started.
		Sensor gsensor = mSensorManager
				.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
		if (gsensor != null) {
			mSensorManager.registerListener(this, gsensor,
					SensorManager.SENSOR_DELAY_NORMAL);
		}

		Sensor msensor = mSensorManager
				.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
		if (msensor != null) {
			mSensorManager.registerListener(this, msensor,
					SensorManager.SENSOR_DELAY_NORMAL);
		}
		if (!mIsScanning)
			mUI.enableGestures(true);
		super.onResumeAfterSuper();
	}

	@Override
	public void onConfigurationChanged(Configuration config) {

	}

	@Override
	public void onStop() {
		stopPreview();
	}

	@Override
	public void installIntentFilter() {
	}

	@Override
	public void onActivityResult(int requestCode, int resultCode, Intent data) {
	}

	@Override
	public boolean onBackPressed() {
		return false;
	}

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		switch (keyCode) {
		case KeyEvent.KEYCODE_VOLUME_UP:
		case KeyEvent.KEYCODE_VOLUME_DOWN:
			return onVolumeKeyDown(event);
		}
		return false;
	}

	private boolean onVolumeKeyDown(KeyEvent event) {
		String[] entryValues = mActivity.getResources().getStringArray(
				R.array.entryvalues_camera_settings_volumne_key_fuction);
		String defValue = mActivity.getResources().getString(
				R.string.camera_setting_item_volume_key_function_default);
		String key = mActivity.getResources().getString(
				R.string.camera_setting_item_volume_key_function_key);
		String function = mPreferences.getString(key, defValue);
		if (function.equals(entryValues[2])) {
			// Volume Adjustment
			return false;
		} else if (function.equals(entryValues[0])) {
			// Shutter
			return true;
		} else if (function.equals(entryValues[1])) {
			// Zoom
			if (mActivity.getCameraState() != CameraActivity.CAMERA_STATE_PREVIEWING
					|| mUI.isScanCardShowing() || mParameters == null) {
				return true;
			}
			if (event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_UP) {
				mUI.zoomIn(mParameters);
			} else if (event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_DOWN) {
				mUI.zoomOut(mParameters);
			}
			return true;
		}
		return false;
	}

	@Override
	public boolean onKeyUp(int keyCode, KeyEvent event) {
		switch (keyCode) {
		case KeyEvent.KEYCODE_VOLUME_DOWN:
		case KeyEvent.KEYCODE_VOLUME_UP:
			return onVolumeKeyUp(event);
		case KeyEvent.KEYCODE_BACK:
			if (!mIsScanning
			// only when in preview we restart scanning
					&& mActivity.getCameraState() == CameraActivity.CAMERA_STATE_PREVIEWING) {
				if (mCameraDevice != null) {
					mHandler.sendEmptyMessage(RESTART_SCAN);
				}
				return true;
			} else {
				return false;
			}
		}
		return false;
	}

	private boolean onVolumeKeyUp(KeyEvent event) {
		String[] entryValues = mActivity.getResources().getStringArray(
				R.array.entryvalues_camera_settings_volumne_key_fuction);
		String defValue = mActivity.getResources().getString(
				R.string.camera_setting_item_volume_key_function_default);
		String key = mActivity.getResources().getString(
				R.string.camera_setting_item_volume_key_function_key);
		String function = mPreferences.getString(key, defValue);
		if (function.equals(entryValues[2])) {
			// Volume Adjustment
			return false;
		} else if (function.equals(entryValues[0])) {
			// Shutter
			return true;
		} else if (function.equals(entryValues[1])) {
			// Zoom
			return true;
		}
		return false;
	}

	@Override
	public void onSingleTapUp(View view, int x, int y) {
	}

	@Override
	public boolean dispatchTouchEvent(MotionEvent m) {
		return mUI.dispatchTouchEvent(m);
	}

	@Override
	public void onPreviewTextureCopied() {

	}

	@Override
	public void onCaptureTextureCopied() {
	}

	@Override
	public void onUserInteraction() {
	}

	@Override
	public boolean updateStorageHintOnResume() {
		return true;
	}

	@Override
	public void onOrientationChanged(int orientation) {
		// We keep the last known orientation. So if the user first orient
		// the camera then point the camera to floor or sky, we still have
		// the correct orientation.
		if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN)
			return;
		mOrientation = Util.roundOrientation(orientation, mOrientation);

		int newOrientation = Util.roundOrientation(orientation, mOrientation);
		if (mOrientation != newOrientation) {
			mOrientation = newOrientation;
		}

		mUI.onOrientationChanged(mOrientation);
	}

	@Override
	public void onShutterButtonFocus(boolean pressed) {

	}

	@Override
	public int onZoomChanged(int index) {
		if (mPaused) {
			return index;
		}
		if (mCameraDevice == null) {
			return index;
		}
		mZoomValue = index;
		mParameters.setZoom(mZoomValue);
		mCameraDevice.setParameters(mParameters);
		Parameters p = mCameraDevice.getParameters();
		if (p != null)
			return p.getZoom();
		return index;
	}

	@Override
	public void cancelAutoFocus() {
		Log.v(TAG, "cancelAutoFocus");
		mCameraDevice.cancelAutoFocus();
		if (mActivity.getCameraState() == CameraActivity.CAMERA_STATE_FOCUSING) {
			mActivity.setCameraState(CameraActivity.CAMERA_STATE_PREVIEWING);
		}
		setCameraParameters(UPDATE_PARAM_PREFERENCE);
	}

	@Override
	public void stopPreview() {
		Log.v(TAG, "stopPreview");
		if (mCameraDevice != null
				&& mActivity.getCameraState() != CameraActivity.CAMERA_STATE_PREVIEW_STOPPED) {
			Log.v(TAG, "CameraDevice stopPreview");
			mCameraDevice.stopPreview();
			mCameraDevice.setPreviewCallback(null);
		}
	}

	@Override
	public int getCameraState() {
		return 0;
	}

	@Override
	public void onSurfaceCreated(SurfaceHolder holder) {

	}

	@Override
	public void onCountDownFinished() {
	}

	@Override
	public void onScreenSizeChanged(int width, int height, int previewWidth,
			int previewHeight) {
	}

	@Override
	public void autoFocus() {
		Log.v(TAG, "autoFocus");
		if (mActivity.getCameraState() == CameraActivity.CAMERA_STATE_PREVIEWING
				&& mCameraDevice != null) {
			Log.v(TAG, "CameraDivice autoFocus");
			mCameraDevice.autoFocus(mAutoFocusCallback);
			mActivity.setCameraState(CameraActivity.CAMERA_STATE_FOCUSING);
			mIsAutoFocusCallback = true;
		}
	}

	@Override
	public boolean capture() {
		return true;
	}

	@Override
	public void startFaceDetection() {
	}

	@Override
	public void stopFaceDetection() {
	}

	@Override
	public void setFocusParameters() {
		setCameraParameters(UPDATE_PARAM_PREFERENCE);
	}

	@Override
	public void onSharedPreferenceChanged() {
	}

	@Override
	public void onRestorePreferencesClicked() {
	}

	@Override
	public void onOverriddenPreferencesClicked() {
	}

	@Override
	public void onCameraPickerClicked(int cameraId) {
	}

	private void updateAutoFocusMoveCallback() {
		if (mParameters.getFocusMode().equals(
				Util.FOCUS_MODE_CONTINUOUS_PICTURE)) {
			mCameraDevice
					.setAutoFocusMoveCallback((AutoFocusMoveCallback) mAutoFocusMoveCallback);
		} else {
			mCameraDevice.setAutoFocusMoveCallback(null);
		}
	}

	private final class AutoFocusCallback implements
			android.hardware.Camera.AutoFocusCallback {
		@Override
		public void onAutoFocus(boolean focused, android.hardware.Camera camera) {
			if (mPaused)
				return;
			if (mActivity.getCameraState() == CameraActivity.CAMERA_STATE_FOCUSING) {
				mActivity
						.setCameraState(CameraActivity.CAMERA_STATE_PREVIEWING);
			}
			mHandler.sendEmptyMessageDelayed(CALL_AUTOFOCUS, AUTOFOCUS_INTERVAL);
		}
	}

	private final class AutoFocusMoveCallback implements
			android.hardware.Camera.AutoFocusMoveCallback {
		@Override
		public void onAutoFocusMoving(boolean moving,
				android.hardware.Camera camera) {
		}
	}

	@Override
	public void restartPreview() {
		Log.v(TAG, "restartPreview");
		if (mPaused)
			return;
		mCameraDevice = mActivity.getCameraDevice();
		mParameters = mCameraDevice.getParameters();
		startPreview();
	}

	@Override
	public void onAccuracyChanged(Sensor arg0, int arg1) {
	}

	@Override
	public void onSensorChanged(SensorEvent event) {
		int type = event.sensor.getType();
		float[] data;
		if (type == Sensor.TYPE_ACCELEROMETER) {
			data = mGData;
		} else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
			data = mMData;
		} else {
			// we should not be here.
			return;
		}
		for (int i = 0; i < 3; i++) {
			data[i] = event.values[i];
		}
		float[] orientation = new float[3];
		SensorManager.getRotationMatrix(mR, null, mGData, mMData);
		SensorManager.getOrientation(mR, orientation);
		mHeading = (int) (orientation[0] * 180f / Math.PI) % 360;
		if (mHeading < 0) {
			mHeading += 360;
		}
	}

	@Override
	public void onCameraFlashModeClicked(int modeId) {
		IconListPreference pref = (IconListPreference) mPreferenceGroup
				.findPreference(CameraSettings.KEY_SCANCAMERA_FLASH_MODE);
		Log.v("mk", "ScanModule, modeId = " + modeId);
		Log.v("mk", "pref == null ? " + (pref == null));

		if (mActivity.getCameraState() == CameraActivity.CAMERA_STATE_FOCUSING) {
			cancelAutoFocus();
		}

		if (pref != null) {
			String value = (String) pref.getEntryValues()[(modeId + 1) % 2];
			pref.setValue(value);
			setFlashParameters();
			mUI.updateFlashButton(value);
		}
	}

	private void loadCameraPreferences() {
		CameraSettings settings = new CameraSettings(mActivity, mInitialParams,
				mCameraId, CameraHolder.instance().getCameraInfo());
		mPreferenceGroup = settings.getPreferenceGroup(R.xml.scan_preferences);
	}

	@Override
	public void onSwipe(int direction) {
		Log.v(CameraActivity.ChangeModuleTag, "onSwipe direction(" + direction
				+ ")");
		if (mPendingModuleIndex != CameraActivity.UNKNOWN_MODULE_INDEX) {
			mActivity.setCachedSwipeEvent(direction);
			return;
		}
		if (mState != State.PREVIEW || mIsScanIntent) {
			return;
		}
		if (direction == PreviewGestures.DIR_LEFT) {
			if (mActivity.getCameraState() == CameraActivity.CAMERA_STATE_PREVIEWING
					|| mActivity.getCameraState() == CameraActivity.CAMERA_STATE_FOCUSING) {
				mPendingModuleIndex = CameraActivity.PANORAMA_MODULE_INDEX;
				mCameraDevice.setOneshotPreviewCallback(this);
				mIsChangeingModule = true;
			}
		} else if (direction == PreviewGestures.DIR_RIGHT) {
			if (mActivity.getCameraState() == CameraActivity.CAMERA_STATE_PREVIEWING
					|| mActivity.getCameraState() == CameraActivity.CAMERA_STATE_FOCUSING) {
				mPendingModuleIndex = CameraActivity.PHOTO_MODULE_INDEX;
				mCameraDevice.setOneshotPreviewCallback(this);
				mIsChangeingModule = true;
			}
		}
		if (mIsChangeingModule) {
			mUI.stopAnimation();
		}
	}

	@Override
	public void onPreviewFrame(byte[] data, Camera camera) {
		if (mPreviewData == null) {
			Log.v(CameraActivity.ChangeModuleTag, "onPreviewFrame scan");
		}

		mPreviewData = data;
		if (mPendingModuleIndex != CameraActivity.UNKNOWN_MODULE_INDEX
				&& mIsChangeingModule) {
			mParameters = mCameraDevice.getParameters();
			Size previewSize = mParameters.getPreviewSize();
			if (mCameraId == 0)
				mActivity.getGLRootView().startFadeIn(previewSize.width,
						previewSize.height, mPreviewData, mCameraId);
			else
				mActivity.getGLRootView().startFadeIn(previewSize.width,
						previewSize.height, mPreviewData, mCameraId);
			mUI.animateToModule(mPendingModuleIndex);
			mIsChangeingModule = false;
		} else {
			Log.d("dyb", "decode");
			mParameters = mCameraDevice.getParameters();
			Size previewSize = mParameters.getPreviewSize();
			if (decodeThread == null)
				return;
			Message message = decodeThread.getHandler().obtainMessage(
					R.id.decode, previewSize.width, previewSize.height, data);
			message.sendToTarget();
			mCameraDevice.setPreviewCallback(null);
		}
	}

	@Override
	public void onFlipAnimationEnd() {
	}

	public Handler getHandler() {
		return mHandler;
	}

	private void onDecodeSuccess(String resultstr) {
		Log.v("mk", "==========================");
		Log.v("mk", "resulttype = " + mResultType);
		Log.v("mk", "resultstr = " + resultstr);
		Log.v("mk", "scan time = " + (System.currentTimeMillis() - mScanTime));
		Log.v("mk", "==========================");
		stopDecode();
		mScanResult = resultstr;
		mUI.setCodeType(mResultType);
		switch (mResultType) {
		case DecodeHandler.RESULT_BAR:
			processBarCodeResult(mScanResult);
			break;
		case DecodeHandler.RESULT_QR:
			processQRCodeResult(resultstr);
			break;
		default:
			break;
		}
	}

	private void processBarCodeResult(String resultstr) {
		mUTCodeType = CODETYPE_BARCODE;
		if (!mIsScanIntent || isProcessScanResult()) {
			boolean networkConnected = Util.isNetworkConnected(mActivity);
			if (networkConnected) {
				mLocation = mLocationManager.getCurrentLocation();
				String postData = NetworkUtil.getPostData(mScanResult,
						(mLocation == null) ? 0 : mLocation.getLongitude(),
						(mLocation == null) ? 0 : mLocation.getLatitude(),
						getUuid(), Util.getNetworkType(mActivity),
						CODETYPE_BARCODE);
				mShareUrl = NetworkUtil.getShareUrl(resultstr);
				Log.v("mk", "post data = " + postData);
				mUI.updateOnlineCard(resultstr, postData);
			} else {
				mUI.updateOfflineCard(ScanCard.TYPE_BARCODE_ERROR, resultstr);
			}
		} else {
			processCodeForInvokation(resultstr);
		}
	}

	private void processQRCodeResult(String resultstr) {
		mUTCodeType = CODETYPE_QRCODE;
		if (!mIsScanIntent || isProcessScanResult()) {
			if (!processQRCodeResultOffline(resultstr)) {
				processQRCodeResultOnline(resultstr);
			}
		} else {
			processCodeForInvokation(resultstr);
		}
	}

	private void processAlipayQrcode() {
		Intent intent = new Intent();
		intent.setAction("android.intent.action.VIEW");
		if (isPackageInstalled("com.eg.android.AlipayGphone")) {
			String url = "alipays://platformapi/startApp?" + "appId=10000007&"
					+ "sourceId=yunos&" + "actionType=route&" + "qrcode="
					+ mScanResult;
			intent.setData(Uri.parse(url));
		} else {
			String url = "http://d.alipay.com/i/index.htm?cid=5230&iframeSrc=alipays%3A%2F%2Fplatformapi%2FstartApp";
			intent.setData(Uri.parse(url));
		}
		startActivityWithSecureMode(intent);
	}

	private void processLaiwangQrcode() {
		Intent intent = new Intent();
		intent.setAction("android.intent.action.VIEW");
		intent.setData(Uri.parse(mScanResult));
		if (isPackageInstalled("com.alibaba.android.babylon")) {
			intent.setPackage("com.alibaba.android.babylon");
		}
		startActivityWithSecureMode(intent);
	}

	private boolean processQRCodeResultOffline(String resultstr) {
		String laiwangPrefix = "http://laiwang.com";
		String alipayPrefix = "https://qr.alipay.com";
		boolean networkConnected = Util.isNetworkConnected(mActivity);
		if (resultstr.startsWith(laiwangPrefix)
				|| resultstr.startsWith(alipayPrefix)) {
			if (resultstr.startsWith(laiwangPrefix)) {
				mUTContentType = CONTENT_TYPE_LAIWANG;
			} else {
				mUTContentType = CONTENT_TYPE_ALIPAY;
			}
			if (networkConnected) {
				return false;
			} else {
				mUI.updateOfflineCard(ScanCard.TYPE_JUMP, resultstr);
				return true;
			}
		} else if (resultstr.startsWith("http://")
				|| resultstr.startsWith("https://")) {
			mUTContentType = CONTENT_TYPE_LINK;
			if (networkConnected) {
				return false;
			} else {
				mUI.updateOfflineCard(ScanCard.TYPE_LINK, resultstr);
				return true;
			}
		} else if (resultstr.startsWith("BEGIN:VCARD")) {
			mUTContentType = CONTENT_TYPE_VCARD;
			mUI.updateOfflineCard(ScanCard.TYPE_VCARD, resultstr);
			return true;
		} else {
			mUTContentType = CONTENT_TYPE_TEXT;
			mUI.updateOfflineCard(ScanCard.TYPE_TEXT, resultstr);
			return true;
		}
	}

	private void processQRCodeResultOnline(String resultstr) {
		mLocation = mLocationManager.getCurrentLocation();
		String postData = NetworkUtil.getPostData(mScanResult,
				(mLocation == null) ? 0 : mLocation.getLongitude(),
				(mLocation == null) ? 0 : mLocation.getLatitude(), getUuid(),
				Util.getNetworkType(mActivity), CODETYPE_QRCODE);
		Log.v("mk", "post data = " + postData);
		mUI.updateOnlineCard(resultstr, postData);
	}

	private void processCodeForInvokation(String result) {
		mUI.updateOfflineCard(ScanCard.TYPE_INVOCATION, result);
	}

	private void playSuccessSound() {
		String key = mActivity
				.getString(R.string.camera_setting_item_shutter_sound_key);
		boolean shutterSound = mPreferences.getBoolean(key, true);
		if (shutterSound) {
			mSoundPool.play(mSoundId, 1.0f, 1.0f, 1, 0, 1.0f);
		}
	}

	private void restartDecode() {
		mIsScanning = true;
		mState = State.PREVIEW;
		mHandler.sendEmptyMessage(DO_SCAN);
		mHandler.sendEmptyMessageDelayed(CALL_AUTOFOCUS, 1000);
		boolean flashSupport = false;
		mParameters = mCameraDevice.getParameters();

		List<String> supportedFlashMode = mParameters.getSupportedFlashModes();
		if (null != supportedFlashMode) {
			for (String s : supportedFlashMode) {
				if (s.equals(Parameters.FLASH_MODE_TORCH)) {
					flashSupport = true;
					break;
				}
			}
		}

		mUI.showScanUI(flashSupport);
	}

	@Override
	public void openBrowser(String url) {
		Log.v("mk", "url = " + url);
		Intent intent = new Intent();
		intent.setAction("android.intent.action.VIEW");
		Uri content_url = Uri.parse(url);
		intent.setData(content_url);
		startActivityWithSecureMode(intent);
	}

	@Override
	public void shareContent() {
		Intent intent = new Intent(Intent.ACTION_SEND);
		intent.setType("text/plain");
		intent.putExtra(Intent.EXTRA_TEXT, mShareUrl);
		intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
		startActivityWithSecureMode(intent);
	}

	@Override
	public void openBarcodeInBrowser() {
		String url = NetworkUtil.getBarcodeGetUrl(mScanResult,
				(mLocation == null) ? 0 : mLocation.getLongitude(),
				(mLocation == null) ? 0 : mLocation.getLatitude(), getUuid(),
				Util.getNetworkType(mActivity), CODETYPE_BARCODE);
		Intent intent = new Intent();
		intent.setAction("android.intent.action.VIEW");
		Uri content_url = Uri.parse(url);
		intent.setData(content_url);
		startActivityWithSecureMode(intent);
	}

	@Override
	public void addContact() {
		// write temp vcard file
		String path = Environment.getExternalStorageDirectory().getPath()
				+ "/.com.yunos.camera";
		File folder = new File(path);
		if (!folder.exists()) {
			folder.mkdir();
		}
		String outFileName = path + "/tmp.vcf";
		FileOutputStream fout;
		try {
			fout = new FileOutputStream(outFileName);
			byte[] bytes = mScanResult.getBytes();
			fout.write(bytes);
			fout.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		File vcfFile = new File(outFileName);
		Uri stuff = Uri.fromFile(vcfFile);
		Intent intent = new Intent(Intent.ACTION_VIEW);

		intent.setDataAndType(stuff, "text/x-vcard");
		intent.setComponent(new ComponentName("com.yunos.alicontacts",
				"com.yunos.alicontacts.vcard.ImportVCardActivity"));
		try {
			mActivity.startActivity(intent);
		} catch (ActivityNotFoundException e) {
			intent.setComponent(null);
			mActivity.startActivity(intent);
		}
		mUI.showContactAdded();
	}

	@Override
	public void jumpAppOffline(String appName) {
		Log.v("mk", "appName = " + appName);
		if ("laiwang".equals(appName)) {
			processLaiwangQrcode();
		} else if ("alipay".equals(appName)) {
			processAlipayQrcode();
		}
	}

	// inherited from ScanResultPage, no use currently.
	public void callContact(String number) {
		Intent intent = new Intent();
		intent.setAction("android.intent.action.CALL");
		intent.setData(Uri.parse("tel:" + number));
		mActivity.startActivity(intent);
	}

	// inherited from ScanResultPage, no use currently
	public void emailContact(String email) {
		Log.d("dyb", "email contact");
		// Uri emailUri = Uri.parse(email);
		Intent intent = new Intent(android.content.Intent.ACTION_SEND);
		intent.putExtra(android.content.Intent.EXTRA_EMAIL,
				new String[] { email });
		intent.setType("plain/text");
		mActivity.startActivity(intent);
	}

	@Override
	public void onShutterButtonClick() {
	}

	@Override
	public void onMediaSaveServiceConnected(MediaSaveService s) {
	}

	@Override
	public void onDestroy() {
	}

	@Override
	public void onAnimationEnd() {
		int pendingEvent = mActivity.getAndClearCachedSwipeEvent();
		if (pendingEvent == PreviewGestures.DIR_LEFT) {
			if (mPendingModuleIndex < 3) {
				mPendingModuleIndex++;
				mUI.animateToModule(mPendingModuleIndex);
				return;
			}
		} else if (pendingEvent == PreviewGestures.DIR_RIGHT) {
			if (mPendingModuleIndex > 0) {
				mPendingModuleIndex--;
				mUI.animateToModule(mPendingModuleIndex);
				return;
			}
		}
		// change module
		mActivity.doChangeCamera(mPendingModuleIndex);
		mPendingModuleIndex = CameraActivity.UNKNOWN_MODULE_INDEX;
	}

	private boolean isPackageInstalled(String packageName) {
		PackageInfo packageInfo;
		try {
			packageInfo = mActivity.getPackageManager().getPackageInfo(
					packageName, 0);
		} catch (NameNotFoundException e) {
			packageInfo = null;
			e.printStackTrace();
		}
		if (packageInfo == null) {
			return false;
		} else {
			return true;
		}
	}

	@Override
	public void frameAvailable() {
		Log.v(CameraActivity.ChangeModuleTag, "frameAvailable scan");
		mActivity.getGLRootView().startFadeOut();
	}

	@Override
	public void cancel() {
		mScanResult = null;
		restartDecode();
	}

	@Override
	public void setResultAndFinish() {
		mActivity.setResult(Activity.RESULT_OK,
				new Intent().putExtra("data", mScanResult));
		mActivity.finish();
	}

	@Override
	public void copyText(String content) {
		ClipboardManager clip = (ClipboardManager) mActivity
				.getSystemService(Context.CLIPBOARD_SERVICE);
		clip.setText(content);
		mUI.showTextCopied();
	}

	private void initializeLocationSettings() {
		mIsRecordLocation = mPreferences.getBoolean(
				CameraSettings.KEY_RECORD_LOCATION, true);
		mLocationManager.recordLocation(mIsRecordLocation);
	}

	private String getUuid() {
		if (mUuid == null || mUuid.length() == 0) {
			mUuid = Util.getUuid();
		}
		return mUuid;
	}

	@Override
	public void clickInOnlineView(String pkgName, String appData, String webUrl) {
		if (pkgName == null) {
			return;
		}
		if (pkgName.equals("browser")) {
			openBrowser(webUrl);
		} else {
			if (appData != null && isPackageInstalled(pkgName)) {
				launchNativeApp(appData);
			} else {
				openBrowser(webUrl);
			}
		}
	}

	public void launchNativeApp(String data) {
		Intent intent = new Intent();
		intent.setAction("android.intent.action.VIEW");
		intent.setData(Uri.parse(data));
		startActivityWithSecureMode(intent);
	}

	private void stopDecode() {
		mUI.hideScanUI();
		mIsScanning = false;
		if (!mIsScanCardInitialized) {
			mIsScanCardInitialized = true;
			mUI.initializeScanCard();
		}
	}

	@Override
	public void jumpAppOnline(String pkgName, String data) {
		Intent intent = new Intent();
		intent.setAction("android.intent.action.VIEW");
		if (isPackageInstalled(pkgName)) {
			intent.setPackage(pkgName);
		}
		intent.setData(Uri.parse(data));
		startActivityWithSecureMode(intent);
	}

	private void startActivityWithSecureMode(Intent intent) {
		try {
			mActivity.startActivity(intent);
		} catch (Exception e) {
			Log.v("mk", "start activity error, " + e.toString());
		} finally {
			if (mActivity.mSecureCamera) {
				mActivity.finish();
			}
		}
	}
}
